package com.ghostbusters.fingertrack.recognition;

import java.util.HashMap;
import java.util.Map;

import net.sf.javaml.core.DenseInstance;
import net.sf.javaml.core.Instance;
import net.sf.javaml.distance.fastdtw.dtw.FastDTW;
import net.sf.javaml.distance.fastdtw.timeseries.TimeSeries;

import org.apache.commons.math.MathException;
import org.apache.commons.math.distribution.TDistribution;
import org.apache.commons.math.distribution.TDistributionImpl;
import org.apache.commons.math.stat.descriptive.moment.Mean;
import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
import org.apache.commons.math.stat.inference.TTestImpl;

import com.ghostbusters.fingertrack.calibration.Calibration;
import com.ghostbusters.fingertrack.calibration.Signature;

public class Recognizer<E> {
	/**
	 * This is the MOST IMPORTANT METHOD! It takes a current calibration, and
	 * the input signature, and returns a map of the possible matches and their
	 * respective probabilities
	 * 
	 * @param calibration
	 *            the calibration you want to compare to
	 * @param signature
	 *            the current input signature
	 * @return a map of possible matches and their probabilities
	 */
	public Map<E, Double> match(Calibration<E> calibration, Signature signature) {
		if (signature.getList().size() > 0) {
			signature.normalize(Calibration.CALIBRATION_LENGTH,
					Calibration.CALIBRATION_SIZE);
			signature.generateBlurredMatrix(0);
			signature.generateRowspace();
		}
		HashMap<E, Double> map = new HashMap<E, Double>();
		Instance i = new DenseInstance(signature.getX());
		TimeSeries main = new TimeSeries(i);
		for (E symbol : calibration.getMap().keySet()) {
			Instance y = new DenseInstance(calibration.getMap().get(symbol)
					.getRowspace());
			TimeSeries t = new TimeSeries(y);

			map.put(symbol, FastDTW.getWarpDistBetween(main, t));
		}
		Map<E, Double> pVals = new HashMap<E, Double>();
		for (E symbol : map.keySet()) {
			Map<E, Double> temp = (Map<E, Double>) map.clone();
			temp.remove(symbol);
			Double[] t = temp.values().toArray(new Double[] {});
			double[] l = new double[t.length];
			for (int j = 0; j < t.length; j++) {
				l[j] = (t[j].doubleValue());
			}
			double value = map.get(symbol);
			StandardDeviation dev = new StandardDeviation();
			double stddev = dev.evaluate(l);
			Mean m = new Mean();
			double mean = m.evaluate(l);
			double z = (value - mean) / stddev;
			TDistribution dist = new TDistributionImpl(l.length - 1);
			try {
				pVals.put(symbol, dist.cumulativeProbability(z));
			} catch (MathException e1) {
				e1.printStackTrace();
			}
		}
		return pVals;
		// signature.normalize(Calibration.CALIBRATION_LENGTH,
		// Calibration.CALIBRATION_SIZE);
		// signature.generateRowspace();
		// Dataset set = new DefaultDataset();
		// for (E symbol : calibration.getMap().keySet()) {
		// double[] values = calibration.getMap().get(symbol).getRowspace();
		// Instance inst = new SparseInstance(values.length, symbol);
		// for (int j = 0; j < values.length; j++) {
		// inst.put(j, values[j]);
		// }
		// set.add(inst);
		// }
		// Dataset input = new DefaultDataset();
		// double[] values = signature.getRowspace();
		// Instance inst = new SparseInstance(values.length);
		// for (int j = 0; j < values.length; j++) {
		// inst.put(j, values[j]);
		// }
		// input.add(inst);
		// System.out.println(inst);
		// Classifier knn = new KNearestNeighbors(5);
		// knn.buildClassifier(set);
		//
		// /*
		// * Load a data set for evaluation, this can be a different one, but we
		// * will use the same one.
		// */
		//
		// /* Counters for correct and wrong predictions. */
		// int correct = 0, wrong = 0;
		// /*
		// * Classify all instances and check with the correct class values
		// */
		// Object predictedClassValue = null;
		// for (Instance i : input) {
		// predictedClassValue = knn.classify(i);
		// knn.
		// }
		// return (E) predictedClassValue;
	}

	public double compareSignatures(Signature sig1, Signature sig2) {
		// TODO actually return something
		return 0;
	}

	public static void t(double mu, double[] value) {

	}

}
